/********************************************************************************
* Copyright (c) 2009, Matteo Pelati (matteo.pelati@gmail.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the <organization> nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Matteo Pelati ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**************************************************************************************/



// dbXmlLib.h

#pragma once

#include <fstream>
#include <string>
#include <cstdlib>
#include <stdlib.h>
#include <stdio.h>
#include <vcclr.h>
#include "dbxml/DbXml.hpp"


using namespace DbXml;
using namespace System;
using namespace System::Collections::Generic;
using namespace std;

namespace BerkeleyDB 
{

	public ref class DbGenericObject
	{
	protected:
		DbGenericObject^ parent;
		DbGenericObject^ manager;
	internal:
		DbGenericObject(DbGenericObject^ parent)
		{
			this->parent = parent;
			if (parent != nullptr) this->manager = parent->Manager;
			else manager = nullptr;
		};
		DbGenericObject(DbGenericObject^ parent, DbGenericObject^ manager)
		{
			this->parent = parent;
			this->manager = manager;
		};
		~DbGenericObject()
		{
			Cleanup();
		};
		virtual void Cleanup()
		{
		
		};
		property DbGenericObject^ Parent
		{
			void set(DbGenericObject^ value)
			{
				this->parent = value;
				if (value != nullptr)
					this->manager = value->Manager;
			}
			DbGenericObject^ get()
			{
				return this->parent;
			}
		};
		property DbGenericObject^ Manager
		{
			DbGenericObject^ get()
			{
				return this->manager;
			}
		};
	};

	public enum class DbXmlDatabaseItemType
	{
		Container,
		Document,
		DocumentSet,
		XmlNode,
		XmlNodeSet,
		UnknownSet
	};

	public interface class IDbXmlDatabaseItem
	{
	public:
		virtual DbXmlDatabaseItemType GetDatabaseItemType();
	};
};

#define CLR_MARSHAL_STRING(x) \
	(char*) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(x).ToPointer()

#define CLR_MARSHAL_CSTRING(x) \
	(char*) System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(x).ToPointer()

#define CLR_MARSHAL_FREE(x) \
	System::Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(x))


#define STDSTR2CLRSTR(varname, val) \
	varname = gcnew String(val.c_str());

#define CLRSTR2STDSTR(varname, val) \
	char* a_##varname = CLR_MARSHAL_CSTRING(val); \
	std::string varname(a_##varname); \
	CLR_MARSHAL_FREE(a_##varname);

#define rethrowDbXmlException() \
	catch(XmlException &e) {throw gcnew DbXmlException(&e);} \
	catch(::DbException &e) {throw gcnew BerkeleyDB::DbException(&e);}\
	catch(exception &e) {throw gcnew Exception(gcnew String(e.what()));} \
	catch(...) {throw gcnew Exception(gcnew String("Unspecified error in the native module: BerkeleyDB.Xml"));}

#define rethrowDbException() \
	catch(::DbException &e) {throw gcnew BerkeleyDB::DbException(&e);}\
	catch(exception &e) {throw gcnew Exception(gcnew String(e.what()));} \
	catch(...) {throw gcnew Exception(gcnew String("Unspecified error in the native module: BerkeleyDB"));}


// Properties definition

#define PROPERTY_VALUETYPE_BOTH(dtype, name, prefix, method) \
	virtual property dtype name \
	{ \
		dtype get() \
		{ \
			dtype value; \
			GetInnerObject()->##prefix##get##method(&value); \
			return value; \
		} \
		void set(dtype value) \
		{ \
			GetInnerObject()->##prefix##set##method(value); \
		}\
	}

#define PROPERTY_VALUETYPE_GET(dtype, name, prefix, method) \
	virtual property dtype name \
	{ \
		dtype get() \
		{ \
			dtype value; \
			GetInnerObject()->##prefix##get##method(&value); \
			return value; \
		} \
	}

#define PROPERTY_VALUETYPE_SET(dtype, name, prefix, method) \
	virtual property dtype name \
	{ \
		void set(dtype value) \
		{ \
			GetInnerObject()->##prefix##set##method(value); \
		}\
	}

#define PROPERTY_UINT32_BOTH(name, prefix, method) PROPERTY_VALUETYPE_BOTH(u_int32_t, name, prefix, method)
#define PROPERTY_UINT32_GET(name, prefix, method) PROPERTY_VALUETYPE_GET(u_int32_t, name, prefix, method)
#define PROPERTY_UINT32_SET(name, prefix, method) PROPERTY_VALUETYPE_SET(u_int32_t, name, prefix, method)

#define PROPERTY_INT_BOTH(name, prefix, method) PROPERTY_VALUETYPE_BOTH(int, name, prefix, method)
#define PROPERTY_INT_GET(name, prefix, method) PROPERTY_VALUETYPE_GET(int, name, prefix, method)
#define PROPERTY_INT_SET(name, prefix, method) PROPERTY_VALUETYPE_SET(int, name, prefix, method)

#define PROPERTY_SIZE_BOTH(name, prefix, method) PROPERTY_VALUETYPE_BOTH(size_t, name, prefix, method)
#define PROPERTY_SIZE_GET(name, prefix, method) PROPERTY_VALUETYPE_GET(size_t, name, prefix, method)
#define PROPERTY_SIZE_SET(name, prefix, method) PROPERTY_VALUETYPE_SET(size_t, name, prefix, method)

#define PROPERTY_LONG_BOTH(name, prefix, method) PROPERTY_VALUETYPE_BOTH(long, name, prefix, method)
#define PROPERTY_LONG_GET(name, prefix, method) PROPERTY_VALUETYPE_GET(long, name, prefix, method)
#define PROPERTY_LONG_SET(name, prefix, method) PROPERTY_VALUETYPE_SET(long, name, prefix, method)


#define PROPERTY_RET_VALUETYPE_BOTH(dtype, name, prefix, method) \
	virtual property dtype name \
	{ \
		dtype get() \
		{ \
			return GetInnerObject()->##prefix##get##method(); \
		} \
		void set(dtype value) \
		{ \
			GetInnerObject()->##prefix##set##method(value); \
		}\
	}

#define PROPERTY_RET_VALUETYPE_GET(dtype, name, prefix, method) \
	virtual property dtype name \
	{ \
		dtype get() \
		{ \
			return GetInnerObject()->##prefix##get##method(); \
		} \
	}

#define PROPERTY_RET_VALUETYPE_SET(dtype, name, prefix, method) \
	virtual property dtype name \
	{ \
		void set(dtype value) \
		{ \
			GetInnerObject()->##prefix##set##method(value); \
		}\
	}

#define PROPERTY_RET_UINT32_BOTH(name, prefix, method) PROPERTY_RET_VALUETYPE_BOTH(u_int32_t, name, prefix, method)
#define PROPERTY_RET_UINT32_GET(name, prefix, method) PROPERTY_RET_VALUETYPE_GET(u_int32_t, name, prefix, method)
#define PROPERTY_RET_UINT32_SET(name, prefix, method) PROPERTY_RET_VALUETYPE_SET(u_int32_t, name, prefix, method)

#define PROPERTY_RET_INT_BOTH(name, prefix, method) PROPERTY_RET_VALUETYPE_BOTH(int, name, prefix, method)
#define PROPERTY_RET_INT_GET(name, prefix, method) PROPERTY_RET_VALUETYPE_GET(int, name, prefix, method)
#define PROPERTY_RET_INT_SET(name, prefix, method) PROPERTY_RET_VALUETYPE_SET(int, name, prefix, method)

#define PROPERTY_RET_SIZE_BOTH(name, prefix, method) PROPERTY_RET_VALUETYPE_BOTH(size_t, name, prefix, method)
#define PROPERTY_RET_SIZE_GET(name, prefix, method) PROPERTY_RET_VALUETYPE_GET(size_t, name, prefix, method)
#define PROPERTY_RET_SIZE_SET(name, prefix, method) PROPERTY_RET_VALUETYPE_SET(size_t, name, prefix, method)

#define PROPERTY_RET_LONG_BOTH(name, prefix, method) PROPERTY_RET_VALUETYPE_BOTH(long, name, prefix, method)
#define PROPERTY_RET_LONG_GET(name, prefix, method) PROPERTY_RET_VALUETYPE_GET(long, name, prefix, method)
#define PROPERTY_RET_LONG_SET(name, prefix, method) PROPERTY_RET_VALUETYPE_SET(long, name, prefix, method)

#define PROPERTY_RET_BOOL_BOTH(name, prefix, method) PROPERTY_RET_VALUETYPE_BOTH(bool, name, prefix, method)
#define PROPERTY_RET_BOOL_GET(name, prefix, method) PROPERTY_RET_VALUETYPE_GET(bool, name, prefix, method)
#define PROPERTY_RET_BOOL_SET(name, prefix, method) PROPERTY_RET_VALUETYPE_SET(bool, name, prefix, method)


#define PROPERTY_RET_OBJ_BOTH(name, dataType, nativeDataType, prefix, method, tnx, qctx) \
	virtual property dataType name \
	{ \
		dataType get() \
		{ \
			try \
			{ \
				nativeDataType *retval = new nativeDataType(GetInnerObject()->##prefix##get##method());\
				if (retval == NULL) return nullptr;\
				else return gcnew dataType(this, retval, tnx, qctx); \
			} \
			catch(nexc &e) \
			{ \
				throw gcnew mexc(&e);\
			} \
		} \
		void set(dataType value) \
		{ \
			GetInnerObject()->##prefix##set##method(value->GetInnerObject()); \
		}\
	}

#define PROPERTY_RET_OBJ_GET(name, dataType, nativeDataType, prefix, method, tnx, qctx) \
	virtual property dataType^ name \
	{ \
		dataType^ get() \
		{ \
			try \
			{ \
			  return gcnew dataType(this, GetInnerObject()->##prefix##get##method(), tnx, qctx); \
			} rethrowDbXmlException() \
		} \
	}

#define PROPERTY_RET_OBJ_SET(name, dataType, nativeDataType, prefix, method) \
	virtual property dataType name \
	{ \
		void set(dataType value) \
		{ \
			GetInnerObject()->##prefix##set##method(value->GetInnerObject()); \
		}\
	}

////

#define PROPERTY_RET_XMLVAL_BOTH(name, dataType, nativeDataType, prefix, method, txn, qctx) \
	virtual property dataType name \
	{ \
		dataType get() \
		{ \
			try \
			{ \
					nativeDataType& rv =  GetInnerObject()->##prefix##get##method(); \
				return ((rv == NULL) ? nullptr : gcnew dataType(this, rv, txn, qctx)); \
			} \
			catch(nexc &e) \
			{ \
				throw gcnew mexc(&e);\
			} \
		} \
		void set(dataType value) \
		{ \
			GetInnerObject()->##prefix##set##method(value->GetInnerObject()); \
		}\
	}

#define PROPERTY_RET_XMLVAL_GET(name, dataType, nativeDataType, prefix, method, txn, qctx) \
	virtual property dataType^ name \
	{ \
		dataType^ get() \
		{ \
			try \
			{ \
					nativeDataType& rv =  GetInnerObject()->##prefix##get##method(); \
				return ((rv == NULL) ? nullptr : gcnew dataType(this, rv, txn, qctx)); \
			} rethrowDbXmlException() \
		} \
	}

#define PROPERTY_RET_XMLVAL_SET(name, dataType, nativeDataType, prefix, method) \
	virtual property dataType name \
	{ \
		void set(dataType value) \
		{ \
			GetInnerObject()->##prefix##set##method(value->GetInnerObject()); \
		}\
	}

////


#define PROPERTY_ENUM_BOTH(name, prefix, method, enumtype, valtype) \
	virtual property enumtype name \
	{ \
		enumtype get() \
		{ \
			valtype value; \
			GetInnerObject()->##prefix##get##method(&value); \
			return (enumtype) value; \
		} \
		void set(enumtype value) \
		{ \
			GetInnerObject()->##prefix##set##method((valtype) value); \
		}\
	}

#define PROPERTY_ENUM_GET(name, prefix, method, enumtype, valtype) \
	virtual property enumtype name \
	{ \
		enumtype get() \
		{ \
			valtype value; \
			GetInnerObject()->##prefix##get##method(&value); \
			return (enumtype) value; \
		} \
	}

#define PROPERTY_ENUM_SET(name, prefix, method, enumtype, valtype) \
	virtual property enumtype name \
	{ \
		void set(enumtype value) \
		{ \
			GetInnerObject()->##prefix##set##method((valtype) value); \
		}\
	}


#define PROPERTY_RET_ENUM_BOTH(name, prefix, method, enumtype, valtype) \
	virtual property enumtype name \
	{ \
		enumtype get() \
		{ \
			return (enumtype) GetInnerObject()->##prefix##get##method(); \
		} \
		void set(enumtype value) \
		{ \
			GetInnerObject()->##prefix##set##method((valtype) value); \
		}\
	}

#define PROPERTY_RET_ENUM_GET(name, prefix, method, enumtype, valtype) \
	virtual property enumtype name \
	{ \
		enumtype get() \
		{ \
			return (enumtype) GetInnerObject()->##prefix##get##method(); \
		} \
	}

#define PROPERTY_RET_ENUM_SET(name, prefix, method, enumtype, valtype) \
	virtual property enumtype name \
	{ \
		void set(enumtype value) \
		{ \
			GetInnerObject()->##prefix##set##method((valtype) value); \
		}\
	}


#define PROPERTY_STRING_BOTH(name, prefix, method) \
	virtual property String^ name \
	{ \
		String^ get() \
		{ \
			const char* val; \
			GetInnerObject()->##prefix##get##method(&val); \
			return gcnew String(val); \
			free((void*)val);\
		} \
		void set(String^ value) \
		{ \
			char* val = CLR_MARSHAL_STRING(value); \
			GetInnerObject()->##prefix##set##method(val); \
			CLR_MARSHAL_FREE(val); \
		} \
	}

#define PROPERTY_STRING_GET(name, prefix, method) \
	virtual property String^ name \
	{ \
		String^ get() \
		{ \
			const char* val; \
			GetInnerObject()->##prefix##get##method(&val); \
			return gcnew String(val); \
			free((void*)val);\
		} \
	}

#define PROPERTY_STRING_SET(name, prefix, method) \
	virtual property String^ name \
	{ \
		void set(String^ value) \
		{ \
			char* val = CLR_MARSHAL_STRING(value); \
			GetInnerObject()->##prefix##set##method(val); \
			CLR_MARSHAL_FREE(val); \
		} \
	}


#define PROPERTY_RET_CSTRING_BOTH(name, prefix, method) \
	virtual property String^ name \
	{ \
		String^ get() \
		{ \
			return gcnew String(GetInnerObject()->##prefix##get##method().c_str());\
		} \
		void set(String^ value) \
		{ \
			char* str = CLR_MARSHAL_CSTRING(value); \
			GetInnerObject()->##prefix##set##method(std::string(str)); \
			CLR_MARSHAL_FREE(str); \
		} \
	}

#define PROPERTY_RET_CSTRING_GET(name, prefix, method) \
	virtual property String^ name \
	{ \
		String^ get() \
		{ \
			return gcnew String(GetInnerObject()->##prefix##get##method().c_str());\
		} \
	}

#define PROPERTY_RET_CSTRING_SET(name, prefix, method) \
	virtual property String^ name \
	{ \
		void set(String^ value) \
		{ \
			char* str = CLR_MARSHAL_CSTRING(value); \
			GetInnerObject()->##prefix##set##method(std::string(str)); \
			CLR_MARSHAL_FREE((void*) str); \
		} \
	}




#define PROPERTY_MEMSIZE_BOTH(name, prefix, method) \
	virtual property DbMemorySize^ name \
	{\
		DbMemorySize^ get() \
		{ \
			u_int32_t gbytes, bytes; \
			GetInnerObject()->##prefix##get##method(&gbytes, &bytes); \
			return gcnew DbMemorySize(gbytes, bytes); \
		} \
		void set(DbMemorySize^ value) \
		{ \
			GetInnerObject()->##prefix##set##method(value->GigaBytes, value->Bytes); \
		} \
	}


